/*
* Copyright 2010 Pavel Kozhin 
*
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed  under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License.
*
* Author: Pavel Kozhin.
*/

package org.etherframework.metadata
{
	import flash.utils.Dictionary;
	
	import org.etherframework.core.IBridge;
	import org.etherframework.core.IControllerPart;
	import org.etherframework.core.ether_internal;
	import org.etherframework.core.util.ClassUtil;
	import org.etherframework.wire.wirings.Autowire;
	import org.etherframework.wire.wirings.Dispatcher;
	import org.etherframework.wire.wirings.EventListener;
	import org.etherframework.wire.wirings.FeedbackListener;
	import org.etherframework.wire.wirings.Hook;
	import org.etherframework.wire.wirings.Mediation;
	import org.etherframework.wire.wirings.MediationListener;
	import org.etherframework.wire.wirings.Proxy;
	import org.etherframework.wire.wirings.Unwired;
	import org.etherframework.wire.wirings.Wired;
	
	use namespace ether_internal;
	
	[ExcludeClass]
	public class MetadataController implements IControllerPart
	{
		private var storage:MetadataStorage;
		
		private var parser:MetadataParser;
		
		private var metatags:Array = [];
		
		private var isInitialized:Boolean = false;
		
		private var util:ClassUtil;
		
	    public function MetadataController(tags:Array)
	    {
			metatags = createMetatags(tags);
	    	storage = new MetadataStorage(new Dictionary());
	    	parser = new MetadataParser();
	    }
	    
	    public function initialize():void
	    {
			if (isInitialized && !_bridge)
				return;
			
			util = bridge.util;
			
			isInitialized = true;
	    }
		
		public function uninitialize():void
		{
			if (!isInitialized)
				return;
			
			isInitialized = false;
		}
	    
		public function addItem(value:Object):Object
		{
			var result:MetadataInfo = null;
	    	
	    	if (value && !storage.getItem(value))
	    	{
	    		result = extractMetadata(value);
	    		storage.addItem(result);
	    	}
	    	
	    	return result;
		}
		
		public function removeItem(value:Object):Object
		{
	    	var result:MetadataInfo = null;
	    	
	    	if (value && (result = storage.getItem(value)))
	    	{
	    		storage.removeItem(result); 
	    	}
	    	
	    	return result; 
		}
		
		public function getItem(value:*):*
		{
			return storage.getItem(value);
		}
		
	    private var _tags:Array = [];
	    public function get tags():Array
	    {
	    	return _tags;
	    }
		
		private var _bridge:IBridge;
		
		public function get bridge():IBridge
		{
			return _bridge;
		}
		
		public function set bridge(value:IBridge):void
		{
			_bridge = value;
		}
		
	    private function extractMetadata(value:*):MetadataInfo
	    {
			var definition:XML = util.describeClassType(value);
			
			if (!definition)
				return null;
			
			if (!metatags.length)
			{
				metatags = [new Metatag(Metatag.AUTOWIRE, Autowire), 
							new Metatag(Metatag.DISPATCHER, Dispatcher), 
							new Metatag(Metatag.EVENT_LISTENER, EventListener), 
							new Metatag(Metatag.PROXY, Proxy), 
							new Metatag(Metatag.MEDIATION, Mediation), 
							new Metatag(Metatag.MEDIATION_LISTENER, MediationListener), 
							new Metatag(Metatag.FEEDBACK_LISTENER, FeedbackListener), 
							new Metatag(Metatag.HOOK, Hook),
							new Metatag(Metatag.WIRED, Wired),
							new Metatag(Metatag.UNWIRED, Unwired)
				];
			}
			
	    	return new MetadataInfo(definition, definition.@name, 
				createMetadata(definition, metatags, parser));
	    }
		
		private function createMetatags(tags:Array):Array
		{
			var tagsLen:int = tags.length;
			var mdata:Metadata;
			var result:Array = [];
			
			for (var i:int = 0; i < tagsLen; ++i)
			{
				mdata = tags[i];
				result = result.concat(mdata.items);
			}
			
			return result;
		}
		
		private function createMetadata(definition:XML, tags:Array, parser:MetadataParser):Array
		{
			var tag:Metatag;
			var max:int = tags.length;
			var metadata:MetatagInfo;
			var result:Array = [];
			var members:Array;
			
			for (var i:int = 0; i < max; ++i)
			{
				tag = tags[i];
				metadata = new MetatagInfo(tag.name, tag.generator);
				members = parser.getVariables(definition, tag.name, true);
				members = members.concat(parser.getVariables(definition, tag.name, false));
				metadata._variables = members;
				
				members = parser.getAccessors(definition, tag.name, true);
				members = members.concat(parser.getAccessors(definition, tag.name, false));
				metadata._accessors = members;
				
				members = parser.getMethods(definition, tag.name, true);
				members = members.concat(parser.getMethods(definition, tag.name, false));
				metadata._methods = members;
				
				metadata._classMetadata = parser.getClassMetaData(definition, tag.name);
				
				if (metadata._variables.length || 
					metadata._accessors.length || 
					metadata._methods.length || 
					metadata._classMetadata.length)
				{
					result.push(metadata);
				}
			}
			
			return result;
		}
	}
}